﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Workflow.ComponentModel;
using System.Collections;
using System.Workflow.ComponentModel.Design;

namespace OpenComposite.Workflow
{
	public static class WorkflowHelpers
	{
		#region MeasureString
		internal static Size MeasureString(
			Graphics g, string text, Font font, StringAlignment alignment, Size maxSize)
		{
			SizeF sizefText = SizeF.Empty;
			if ( maxSize == Size.Empty ) {
				sizefText = g.MeasureString(text, font);
			} else {
				using ( StringFormat sf = new StringFormat(StringFormat.GenericTypographic) ) {
					sf.Alignment = alignment;
					sf.LineAlignment = StringAlignment.Center;
					sf.Trimming = StringTrimming.EllipsisCharacter;
					sf.FormatFlags = StringFormatFlags.NoClip | StringFormatFlags.FitBlackBox | StringFormatFlags.NoWrap;
					sizefText = g.MeasureString(text, font, maxSize, sf);
				}
			}
			return Size.Ceiling(sizefText) + new Size(10, 4);
		}
		#endregion

		#region GetCenter
		/// <summary>
		/// Returns a System.Drawing.Point representing the center of the System.Drawing.Rectangle
		/// </summary>
		/// <param name="r">System.Drawing.Rectangle to process</param>
		/// <returns>A System.Drawing.Point representing the center of the given System.Drawing.Rectangle</returns>
		public static Point GetCenter(Rectangle r)
		{
			return new Point(r.Left + r.Width / 2, r.Top + r.Height / 2);
		}
		/// <summary>
		/// Returns a System.Drawing.PointF representing the center of the System.Drawing.RectangleF
		/// </summary>
		/// <param name="r">System.Drawing.RectangleF to process</param>
		/// <returns>A System.Drawing.PointF representing the center of the given System.Drawing.RectangleF</returns>
		public static PointF GetCenter(RectangleF r)
		{
			return new PointF(r.Left + r.Width / 2, r.Top + r.Height / 2);
		}
		#endregion

		#region ToPoint

		/// <summary>
		/// Converts a System.Drawing.PointF to a System.Drawing.Point (using rounding)
		/// </summary>
		/// <param name="p">System.Drawing.PointF to convert.</param>
		/// <returns>The System.Drawing.Point result of the conversion-</returns>
		public static Point ToPoint(PointF p)
		{
			return Point.Round(p);
		}

		#endregion

		#region ShuffleList

		/// <summary>
		/// Shuffles a System.Collection.Generics.List<Activity> inplace
		/// </summary>
		/// <param name="listToShuffle">extended object</param>
		/// <returns></returns>
		public static IList<T> ShuffleList<T>(IList<T> listToShuffle)
		{
			Random rand = new Random();
			for ( int k = listToShuffle.Count - 1; k > 1; --k ) {
				int randIndx = rand.Next(k); //
				T temp = listToShuffle[k];
				listToShuffle[k] = listToShuffle[randIndx]; // move random num to  end of list.
				listToShuffle[randIndx] = temp;
			}
			return listToShuffle;
		}

		#endregion

		#region GetRootActivity
		/// <summary>
		/// Gets the root activity from a specified entry point activity down to the root acitivty
		/// </summary>
		/// <param name="current">Entry point activity</param>
		/// <returns>The root activity</returns>
		internal static Activity GetRootActivity(Activity activity)
		{
			if ( activity == null ) throw new ArgumentNullException("activity");

			while ( activity.Parent != null ) activity = activity.Parent;

			return activity;
		}
		#endregion

		#region GetPreceedingActivities
		/// <summary>
		/// Returns a Array of Activities, which are located before the given Activity in the workflow
		/// </summary>
		/// <param name="act">Given Activity</param>
		/// <returns>Array of Activities preceeding the given Activity</returns>
		public static Activity[] GetPreceedingActivities(Activity act)
		{
			// TODO: This is not the final implementation it returns all activities instead of preceeding activities
			CompositeActivity root = GetRootActivity(act) as CompositeActivity;
			Activity[] preceeding = new Activity[0];
			if ( root != null ) {
				preceeding = GetNestedActivities(root);
			}
			return preceeding;
		}
		#endregion

		#region IsFrameworkActivity
		/// <summary>
		/// Used for horizontal workflow exception handling
		/// </summary>
		/// <param name="activity">Activity to check</param>
		/// <returns>true if it is a AlternateFlowActivity, false if it is a normal Activity</returns>
		internal static bool IsFrameworkActivity(Activity activity)
		{
			if ( activity is HorizontalCancellationHandlerActivity ||
				 activity is HorizontalFaultHandlersActivity ||
				 activity is HorizontalCompensationHandlerActivity )
				return true;
			else
				return false;
		}
		#endregion

		#region GetActivityHierarchy
		/// <summary>
		/// Gets the activity hirarchy from the specified entry point activity down to the root activity.
		/// </summary>
		/// <param name="child">Entry point activity</param>
		/// <returns>The List<string> representing the top-down hirarchy of activities</returns>
		internal static List<string> GetActivityHierarchy(Activity child)
		{
			Activity parent = child.Parent;
			List<string> outList = new List<string>();
			while ( parent != null ) {
				outList.Add(parent.QualifiedName);
				parent = parent.Parent;
			}
			outList.Reverse();
			outList.RemoveAt(0);
			outList.Add(child.QualifiedName);

			return outList;
		}
		#endregion

		#region GetActivityByName
		public static Activity GetActivityByName(Activity root, string value)
		{
			CompositeActivity croot = root as CompositeActivity;
			if ( croot == null ) return null;
			Activity[] activities = GetNestedActivities(croot);
			foreach ( Activity act in activities ) {
				if ( act.Name == value ) return act;
			}
			return null;
		}
		#endregion

		#region GetRootDesigner
		/// <summary>
		/// Gets the root designer of the specified designer.
		/// </summary>
		/// <param name="current">Designer to check</param>
		/// <returns>Root designer</returns>
		public static ActivityDesigner GetRootDesigner(ActivityDesigner current)
		{
			if ( current == null ) throw new ArgumentNullException("current");

			ActivityDesigner designer = current;

			while ( designer.ParentDesigner != null ) {
				designer = designer.ParentDesigner;
			}
			return designer;
		}
		#endregion

		#region GetTopMostExpanded
		/// <summary>
		/// Gets the top most expanded designer. Used for drawing FreeConnectors.
		/// </summary>
		/// <param name="designer">Designer to check</param>
		/// <returns>Top most designer, which is not collapsed</returns>
		internal static ActivityDesigner GetTopMostExpanded(ActivityDesigner designer)
		{
			HorizontalWorkflowDesigner root = GetRootDesigner(designer) as HorizontalWorkflowDesigner;
			if ( designer.ParentDesigner is HorizontalWorkflowDesigner ) {
				return designer;
			} else {
				CompositeActivityDesigner outDesigner = null;

				List<ActivityDesigner> path = GetDesignerHierarchy(designer);
				foreach ( ActivityDesigner d in path ) {
					if ( d is CompositeActivityDesigner ) {
						CompositeActivityDesigner cd = d as CompositeActivityDesigner;
						if ( !cd.Expanded ) {
							outDesigner = cd;
							break;
						}
					}
				}
				if ( outDesigner == null )
					return designer;
				else
					return outDesigner as ActivityDesigner;

			}

		}
		#endregion

		#region HasInvalidJumpTarget
		/// <summary>
		/// Checks if the specified designer is an invalid jump target
		/// </summary>
		/// <param name="designer">Target designer to check</param>
		/// <returns>true if designer is invalid jump target, false if designer is valid jump target</returns>
		internal static bool HasInvalidJumpTarget(ActivityDesigner designer)
		{
			bool isInvalid = false;
			CompositeActivityDesigner parent = designer.ParentDesigner;
			while ( parent.ParentDesigner != null ) {
				if ( parent is ParallelSplitDesigner ) {
					if ( ( (ParallelActivity)parent.Activity ).Count > 1 ) {
						isInvalid = true;
						break;
					}
				} else if ( parent is InclusiveORDesigner ) {
					isInvalid = true;
					break;
				}
				parent = parent.ParentDesigner;
			}

			return isInvalid;
		}
		#endregion

		#region GetDesignerHierarchy
		/// <summary>
		/// Gets the hirarchy of the designers, from specified entry point down to the root element
		/// </summary>
		/// <param name="designer">Indicates the entry point of the method</param>
		/// <returns>Hirarchy of the designers</returns>
		internal static List<ActivityDesigner> GetDesignerHierarchy(ActivityDesigner designer)
		{
			ActivityDesigner parent = designer.ParentDesigner;
			List<ActivityDesigner> outList = new List<ActivityDesigner>();
			while ( parent != null ) {
				outList.Add(parent.ParentDesigner);
				parent = parent.ParentDesigner;
			}
			outList.Reverse();
			if ( outList.Count > 0 ) {
				outList.RemoveAt(0);
			}
			outList.Add(designer);

			return outList;
		}
		#endregion

		#region GetAlternateFlowActivity
		/// <summary>
		/// Gets the Activity of a specified System.Type in an ActivityCollection
		/// </summary>
		/// <param name="activities">ActivityCollection to check</param>
		/// <param name="t">Type of the Activity</param>
		/// <returns>Alternate Flow Activity</returns>
		internal static Activity GetAlternateFlowActivity(ActivityCollection activities, Type t)
		{
			object[] obj = t.GetCustomAttributes(typeof(AlternateFlowActivityAttribute), true);

			foreach ( Activity activity in activities ) {
				if ( activity.GetType() == t ) return activity;
				else continue;
			}
			return null;
		}
		#endregion

		#region CompleteCloseContext
		/// <summary>
		/// Closes the given ActivityExecutionContext
		/// </summary>
		/// <param name="context">Context to close</param>
		internal static void CompleteCloseContext(ActivityExecutionContext context, EventHandler<ActivityExecutionStatusChangedEventArgs> closedEvent)
		{
			int i = 0;
			try {
				while ( context.ExecutionContextManager.ExecutionContexts.Count != i ) {
					if ( context.ExecutionContextManager.ExecutionContexts[i].Activity.ExecutionStatus == ActivityExecutionStatus.Closed ) {
						context.ExecutionContextManager.CompleteExecutionContext(context.ExecutionContextManager.ExecutionContexts[i]);
					} else if ( context.Activity.ExecutionStatus == ActivityExecutionStatus.Executing ||
								context.Activity.ExecutionStatus == ActivityExecutionStatus.Canceling ||
								context.Activity.ExecutionStatus == ActivityExecutionStatus.Compensating ||
								context.Activity.ExecutionStatus == ActivityExecutionStatus.Closed ) {
						context.CloseActivity();
						//for eventhandling... correct here? seems so.
						i++;
					} else {
						i++;
					}
				}
				if ( context.ExecutionContextManager.ExecutionContexts.Count == 0 ) {
					SecureCloseContext(context, closedEvent);
				}
			} catch ( Exception ex ) {
				System.Windows.Forms.MessageBox.Show("Close context:" + Environment.NewLine + Environment.NewLine + ex.ToString());
			}
		}
		/// <summary>
		/// Closes the given ActivityExecutionContext
		/// </summary>
		/// <param name="context">Context to close</param>
		internal static void CompleteCloseContext(ActivityExecutionContext context)
		{
			int i = 0;
			try {
				while ( context.ExecutionContextManager.ExecutionContexts.Count != i ) {
					if ( context.ExecutionContextManager.ExecutionContexts[i].Activity.ExecutionStatus == ActivityExecutionStatus.Closed ) {
						context.ExecutionContextManager.CompleteExecutionContext(context.ExecutionContextManager.ExecutionContexts[i]);
					} else if ( context.Activity.ExecutionStatus == ActivityExecutionStatus.Executing ||
								context.Activity.ExecutionStatus == ActivityExecutionStatus.Canceling ||
								context.Activity.ExecutionStatus == ActivityExecutionStatus.Compensating ||
								context.Activity.ExecutionStatus == ActivityExecutionStatus.Closed ) {
						context.CloseActivity();
						//for eventhandling... correct here?
						i++;
					} else {
						i++;
					}
				}
				if ( context.ExecutionContextManager.ExecutionContexts.Count == 0 ) {
					SecureCloseContext(context);
				}
			} catch ( Exception ex ) {
				System.Windows.Forms.MessageBox.Show("Close context:" + Environment.NewLine + Environment.NewLine + ex.ToString());
			}
		}
		#endregion

		#region SecureCloseContext
		internal static void SecureCloseContext(ActivityExecutionContext context)
		{
			SecureCloseContext(context, null);
		}
		/// <summary>
		/// Closes the context only if it is possible.
		/// </summary>
		/// <param name="context">The context to close</param>
		/// <param name="closedEvent">The event to subscribe to the Closed Event of the context activity. null for no Event.</param>
		internal static void SecureCloseContext(ActivityExecutionContext context, EventHandler<ActivityExecutionStatusChangedEventArgs> closedEvent)
		{
			try {
				if ( context.Activity.ExecutionStatus == ActivityExecutionStatus.Executing ||
					 context.Activity.ExecutionStatus == ActivityExecutionStatus.Canceling ||
					 context.Activity.ExecutionStatus == ActivityExecutionStatus.Compensating ||
					 context.Activity.ExecutionStatus == ActivityExecutionStatus.Closed ) {
					if ( closedEvent != null ) {
						context.Activity.Closed += closedEvent;
					}
					context.CloseActivity();
				} else { }
			} catch ( Exception ex ) {
				System.Windows.Forms.MessageBox.Show("SecureCloseContext: " + Environment.NewLine + Environment.NewLine + ex.ToString());
			}
		}
		#endregion

		#region GetNestedActivities
		public static Activity[] GetNestedActivities(CompositeActivity compositeActivity)
		{
			if ( compositeActivity == null ) throw new ArgumentNullException("compositeActivity");

			IList<Activity> childActivities = null;
			ArrayList nestedActivities = new ArrayList();
			Queue compositeActivities = new Queue();
			compositeActivities.Enqueue(compositeActivity);
			while ( compositeActivities.Count > 0 ) {
				CompositeActivity compositeActivity2 = (CompositeActivity)compositeActivities.Dequeue();
				childActivities = compositeActivity2.Activities;

				foreach ( Activity activity in childActivities ) {
					nestedActivities.Add(activity);
					if ( activity is CompositeActivity ) compositeActivities.Enqueue(activity);
				}
			}
			return (Activity[])nestedActivities.ToArray(typeof(Activity));
		}
		#endregion

		#region EnsureUniqueName
		/// <summary>
		/// Returns a unique name for the Activity within the root component of the given Activity.
		/// </summary>
		/// <param name="activity">The activity.</param>
		/// <returns></returns>
		public static string EnsureUniqueName(Activity activity)
		{
			return EnsureUniqueName(activity, activity.Name);
		}
		/// <summary>
		/// Returns a unique name for the Activity within the root component of the given Activity.
		/// </summary>
		/// <param name="activity">The activity.</param>
		/// <param name="name">The name.</param>
		/// <returns></returns>
		public static string EnsureUniqueName(Activity activity, string name)
		{
			CompositeActivity root = GetRootActivity(activity) as CompositeActivity;
			if ( root == null ) return name;
			return EnsureUniqueName(root, name);
		}
		/// <summary>
		/// Returns a unique name for the Activity within the given CompositeActivity.
		/// </summary>
		/// <param name="parentActivity">The parent activity.</param>
		/// <param name="activity">The activity.</param>
		/// <returns></returns>
		public static string EnsureUniqueName(CompositeActivity parentActivity, Activity activity)
		{
			return EnsureUniqueName(parentActivity, activity.Name);
		}
		/// <summary>
		/// Returns a unique name for the Activity within the given CompositeActivity.
		/// </summary>
		/// <param name="parentActivity">The parent activity.</param>
		/// <param name="name">The name.</param>
		/// <returns></returns>
		public static string EnsureUniqueName(CompositeActivity parentActivity, string name)
		{
			string cleanString = CleanString(name);
			List<string> names = GetChildActivityNames(parentActivity);
			if ( !names.Contains(cleanString) ) return cleanString;
			int i = 0;
			while ( names.Contains(string.Format("{0}{1}", cleanString, i)) ) {
				i++;
			}
			return string.Format("{0}{1}", cleanString, i);
		}
		#endregion

		#region CleanString
		private static List<char> _validIdentifierCharacters =
			new List<char>(new char[]{
				'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
				'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z',
				'1','2','3','4','5','6','7','8','9','0','_'
			});

		/// <summary>
		/// Removes invalid characters from the <see cref="T:System.String"/>, making it a C#-valid identifier and returns the result.
		/// </summary>
		/// <param name="p">The <see cref="T:System.String"/> to clean.</param>
		/// <returns></returns>
		public static string CleanString(string p)
		{
			string value = p;
			foreach ( char c in value ) {
				if ( !_validIdentifierCharacters.Contains(c) ) {
					value = value.Replace(c.ToString(), "");
				}
			}
			// a digit cannot be first character of an identifier
			if ( value.Length > 0 && char.IsDigit(value[0]) ) {
				value = string.Format("_{0}", value);
			}
			return value;
		}
		#endregion

		#region MakeNameWithSpaces
		/// <summary>
		/// Returns a copy of the <see cref="T:System.String"/> that has spaces at specific locations. (Before capital letters, not before first character, not between two capital letters, before digits, not between two digits, not before first digit)
		/// </summary>
		/// <param name="name">The name to convert.</param>
		/// <returns></returns>
		public static string MakeNameWithSpaces(string name)
		{
			bool spaced = false;
			name = name.Replace('_', ' ');
			for ( int i = 0; i < name.Length; i++ ) {
				if ( i == 0 ) continue;
				if ( i == name.Length ) continue;
				if ( spaced ) { spaced = false; continue; }
				char c = name[i];
				if ( c == ' ' ) continue;
				if ( char.IsUpper(name, i) || char.IsNumber(name, i) ) {
					if ( i > 0 && char.IsUpper(name, i - 1) || char.IsNumber(name, i) ) continue;
					if ( i > 3 && ( char.IsSymbol(name, i - 1) || char.IsWhiteSpace(name, i - 1) ) ) continue;
					name = name.Insert(i, " ");
					spaced = true;
					i++;
				} else if ( char.IsLower(name, i) ) {
					if ( i > 1 && char.IsUpper(name, i - 1) ) {
						if ( i > 3 && name[i - 2] == ' ' ) continue;
						name = name.Insert(i - 1, " ");
						spaced = true;
						i++;
					}
				}
			}
			return name;
		}
		#endregion

		#region GetChildActivityNames
		/// <summary>
		/// Returns a list with the names of all child activities of the given CompositeActivity
		/// </summary>
		/// <param name="activity">The activity.</param>
		/// <returns></returns>
		public static List<string> GetChildActivityNames(CompositeActivity activity)
		{
			List<string> names = new List<string>();
			if ( activity == null ) return names;
			foreach ( Activity child in activity.Activities ) {
				names.Add(child.Name);
				if ( child is CompositeActivity ) {
					names.AddRange(GetChildActivityNames(child as CompositeActivity));
				}
			}
			return names;
		}
		#endregion

		#region Public Events
		public static event EventHandler AssignWorkflow;

		internal static void OnAssignWorkflow(LogicalCapabilityActivity logAct)
		{
			EventHandler tmp = AssignWorkflow;
			if ( tmp != null ) {
				tmp(logAct, EventArgs.Empty);
			}
		}
		#endregion
	}
}
